package com.demo.controller;

import com.alibaba.fastjson.JSON;
import com.demo.dao.repository.OrderRepository;
import com.demo.model.Order;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ScrolledPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

@Slf4j
@RestController
public class EsController {

    @Resource
    private OrderRepository orderRepository;

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @RequestMapping(value = "/saveOrder/{orderSn}",method = RequestMethod.GET)
    public void saveOrder(@PathVariable("orderSn") String orderSn) {
        Order order = new Order();
        order.setOrderSn(orderSn);
        order.setOrderId(System.currentTimeMillis());
        order.setDateOfPurchase(new Date());
        order.setAddress(orderSn + " - 地址");
        order.setPrice(BigDecimal.ONE);

        orderRepository.save(order);
    }

    @RequestMapping(value = "/queryOrder/{orderSn}",method = RequestMethod.GET)
    public List<Order> queryOrder(@PathVariable("orderSn") String orderSn) {
        return orderRepository.findByOrderSnLike(orderSn);
    }

    @RequestMapping(value = "/updateOrder",method = RequestMethod.POST)
    public void updateOrder(@RequestParam(name = "orderSn") String orderSn,
                            @RequestParam(name = "address") String address,
                            @RequestParam(name = "price") BigDecimal price) {
        List<Order> orders = orderRepository.findByOrderSnLike(orderSn);
        orders.forEach(s -> {
            s.setAddress(address);
            s.setPrice(price);
        });

        orderRepository.saveAll(orders);
    }

    /**
     * BoolQueryBuilder.must 相当于 and
     * @param orderSn
     * @param address
     * @return
     */
    @PostMapping(value = "/queryOrderWithCondition")
    public List<Order> queryOrderWithCondition(@ApiParam(name = "orderSn", value = "订单号") @RequestParam(name = "orderSn", required = false) String orderSn,
                                               @ApiParam(name = "address", value = "地址") @RequestParam(name = "address", required = false) String address,
                                               @ApiParam(name = "startPrice", value = "范围查询最低价格") @RequestParam(name = "startPrice", required = false) String startPrice,
                                               @ApiParam(name = "endPrice", value = "范围查询最高价格") @RequestParam(name = "endPrice", required = false) String endPrice) {

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotBlank(orderSn)) {
            // 精准查询
            boolQueryBuilder.must(QueryBuilders.termQuery("orderSn", orderSn));
        }

        if (StringUtils.isNotBlank(address)) {
            // 模糊查询
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("address", String.format("*%s*", address)));
        }

        if (StringUtils.isNotBlank(startPrice)) {
            // 范围查询，大于
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(startPrice));
        }

        if (StringUtils.isNotBlank(endPrice)) {
            // 范围查询，小于
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lt(endPrice));
        }

        SearchQuery query = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder).build();

        Page<Order> page = orderRepository.search(query);
        log.info("es 结果：{}", JSON.toJSONString(page));
        return page.getContent();
    }

    /**
     * BoolQueryBuilder.should 相当于 or
     * @param orderSn
     * @param address
     * @return
     */
    @PostMapping(value = "/queryOrderWithCondition1")
    public List<Order> queryOrderWithCondition1(@ApiParam(name = "orderSn", value = "订单号") @RequestParam(name = "orderSn", required = false) String orderSn,
                                               @ApiParam(name = "address", value = "地址") @RequestParam(name = "address", required = false) String address) {

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotBlank(orderSn)) {
            // 模糊查询
            boolQueryBuilder.should(QueryBuilders.wildcardQuery("orderSn", String.format("*%s*", orderSn)));
        }

        if (StringUtils.isNotBlank(address)) {
            // 模糊查询
            boolQueryBuilder.should(QueryBuilders.wildcardQuery("address", String.format("*%s*", address)));
        }

        SearchQuery query = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder).build();

        Page<Order> page = orderRepository.search(query);
        log.info("es 结果：{}", JSON.toJSONString(page));
        return page.getContent();
    }

    /**
     * es分页， from + size分页查询只能查es前1w条记录,
     * 因为ES使用index.max_result_window:10000作爲保护措施，这个参数可以动态修改，也可以在配置文件配置
     * @param pageNo
     * @param pageSize
     * @return
     */
    @PostMapping(value = "/queryOrderByPage")
    public List<Order> queryOrderByPage(@ApiParam(name = "pageNo", value = "页码") @RequestParam(name = "pageNo", required = false) int pageNo,
                                        @ApiParam(name = "pageSize", value = "每页记录数") @RequestParam(name = "pageSize", required = false) int pageSize) {

        PageRequest pageRequest = PageRequest.of(pageNo, pageSize);

        SearchQuery query = new NativeSearchQueryBuilder().withPageable(pageRequest).build();
        Page<Order> search = orderRepository.search(query);
        List<Order> content = search.getContent();
        log.info("queryOrderByPage es 结果：{}", JSON.toJSONString(content));
        return content;
    }

    /**
     * es分页，使用scroll分页，数据量大，性能会变差，但是不受from + size分页限制。
     * 因为scroll是通过迭代查询数据
     * @param pageNo
     * @param pageSize
     * @return
     */
    @PostMapping(value = "/queryOrderByScroll")
    public List<Order> queryOrderByScroll(@ApiParam(name = "pageNo", value = "页码") @RequestParam(name = "pageNo", required = false) int pageNo,
                                          @ApiParam(name = "pageSize", value = "每页记录数") @RequestParam(name = "pageSize", required = false) int pageSize,
                                          @ApiParam(name = "address", value = "地址") @RequestParam(name = "address", required = false) String address) {

        PageRequest pageRequest = PageRequest.of(pageNo, pageSize);
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotBlank(address)) {
            // 精准查询
            boolQueryBuilder.must(QueryBuilders.termQuery("address", address));
        }

        SearchQuery query = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder).withPageable(pageRequest).build();

        ScrolledPage<Order> result = null;
        List<Order> content = null;
        try {
            // 深度查询分页，Scroll会在es服务器上生成一个快照，保存一定的时间
            result = this.elasticsearchRestTemplate.startScroll(5000, query, Order.class);
            // 通过迭代方式查找指定分页数据
            for (int i = 0; i < pageNo; i++) {
                content = result.getContent();
                //取下一页，scrollId在es服务器上可能会发生变化，需要用最新的。发起continueScroll请求会重新刷新快照保留时间
                result = elasticsearchRestTemplate.continueScroll(result.getScrollId(), 30000, Order.class);
            }
        } finally {
            if (result != null) {
                // 最后释放查询
                elasticsearchRestTemplate.clearScroll(result.getScrollId());
            }
        }
        log.info("queryOrderByScroll es 结果：{}", JSON.toJSONString(content));

        return content;
    }
}
